package cn.schoolwow.quickserver.server;

import cn.schoolwow.quickserver.controller.ExceptionHandler;
import cn.schoolwow.quickserver.controller.RequestMethod;
import cn.schoolwow.quickserver.controller.ResponseBodyAdvice;
import cn.schoolwow.quickserver.controller.annotation.CrossOrigin;
import cn.schoolwow.quickserver.controller.annotation.RequestMapping;
import cn.schoolwow.quickserver.controller.annotation.RestController;
import cn.schoolwow.quickserver.domain.Client;
import cn.schoolwow.quickserver.domain.ControllerMeta;
import cn.schoolwow.quickserver.domain.HttpSessionMeta;
import cn.schoolwow.quickserver.domain.ServerConfigMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class HttpServer {
    Logger logger = LoggerFactory.getLogger(HttpServer.class);

    private ServerConfigMeta serverConfigMeta;

    public HttpServer(ServerConfigMeta serverConfigMeta) {
        this.serverConfigMeta = serverConfigMeta;
    }

    /**
     * 启动http服务
     * */
    public void start() throws IOException {
        getControllerMetaList();
        startKeepAliveScheduleTask();
        startHttpSessionScheduleTask();
        ServerSocket serverSocket = new ServerSocket();
        InetSocketAddress inetSocketAddress = new InetSocketAddress(serverConfigMeta.port);
        serverSocket.bind(inetSocketAddress);
        logger.info("[服务器启动]{}", inetSocketAddress);
        while (true) {
            final Socket socket = serverSocket.accept();
            logger.debug("[接收客户端Socket链接]{}",socket);
            serverConfigMeta.threadPoolExecutor.execute(new HttpClientThread(socket,serverConfigMeta));
        }
    }

    /**
     * 开启会话定时任务检查
     * */
    private void startHttpSessionScheduleTask(){
        serverConfigMeta.scheduledExecutorService.scheduleWithFixedDelay(() -> {
            Set<Map.Entry<String, HttpSessionMeta>> entrySet = serverConfigMeta.sessionMap.entrySet();
            entrySet.removeIf(entry -> (System.currentTimeMillis() - entry.getValue().lastAccessedTime) / 1000 >= serverConfigMeta.sessionTimeout);
        }, 60, 600, TimeUnit.SECONDS);
    }

    /**
     * 开启keepAlive定时任务检查
     * */
    private void startKeepAliveScheduleTask(){
        if(!serverConfigMeta.keepAlive){
            return;
        }
        logger.trace("[开启keepAlive定时检查任务]");
        serverConfigMeta.scheduledExecutorService.scheduleWithFixedDelay(() -> {
            //检查客户端上次活跃事件,超过指定超时时间的就关闭
            Iterator<Client> clientIterator = serverConfigMeta.keepAliveClientList.iterator();
            while(clientIterator.hasNext()){
                Client client = clientIterator.next();
                if(client.socket.isClosed()){
                    logger.trace("[keepAlive]链接已经关闭,从缓存中移除!");
                    clientIterator.remove();
                }else{
                    logger.trace("[keepAlive]检查超时时间.上次活跃时间:{},套接字:{}",new Date(client.lastActiveTime),client.socket);
                    if(System.currentTimeMillis()-client.lastActiveTime>=serverConfigMeta.keepAliveTimeout){
                        logger.debug("[keepAlive]关闭不活跃客户端,上次活跃时间:{},套接字:{}",new Date(client.lastActiveTime),client.socket);
                        try {
                            client.socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        clientIterator.remove();
                    }
                }
            }
        }, 3000, serverConfigMeta.keepAliveTimeout/2, TimeUnit.MILLISECONDS);
    }

    /**
     * 提取Controller信息
     */
    private void getControllerMetaList() {
        serverConfigMeta.applicationContext.refresh();
        String[] beanNames = serverConfigMeta.applicationContext.getBeanNamesForAnnotation(RestController.class);
        for (String beanName : beanNames) {
            Object instance = serverConfigMeta.applicationContext.getBean(beanName);
            String bathUrl = "";
            RequestMapping classRequestMapping = instance.getClass().getDeclaredAnnotation(RequestMapping.class);
            if (classRequestMapping != null) {
                bathUrl = classRequestMapping.value();
            }
            Method[] methods = instance.getClass().getMethods();
            for (Method method : methods) {
                RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
                if (methodRequestMapping == null) {
                    continue;
                }
                ControllerMeta controllerMeta = new ControllerMeta();
                controllerMeta.instance = instance;
                controllerMeta.mappingUrl = bathUrl + methodRequestMapping.value();
                controllerMeta.requestMethodList.addAll(Arrays.asList(methodRequestMapping.method()));
                //跨域方法添加OPTION请求支持
                if (null != method.getAnnotation(CrossOrigin.class) || null != method.getDeclaringClass().getAnnotation(CrossOrigin.class)) {
                    controllerMeta.requestMethodList.add(RequestMethod.OPTIONS);
                }
                controllerMeta.antPatternUrl = controllerMeta.mappingUrl.replaceAll("\\{\\w+\\}", "\\*");
                controllerMeta.method = method;
                serverConfigMeta.controllerMetaList.add(controllerMeta);
                logger.info("[映射路径][{},method={}] onto {}", controllerMeta.mappingUrl, controllerMeta.requestMethodList, method);
            }
        }
        try {
            serverConfigMeta.responseBodyAdvice = serverConfigMeta.applicationContext.getBean(ResponseBodyAdvice.class);
        } catch (NoSuchBeanDefinitionException e) {
        }
        try {
            serverConfigMeta.exceptionHandler = serverConfigMeta.applicationContext.getBean(ExceptionHandler.class);
        } catch (NoSuchBeanDefinitionException e) {
        }
    }
}
